/*
** $Id: luac.c,v 1.75 2015/03/12 01:58:27 lhf Exp $
** Lua compiler (saves bytecodes to files; also lists bytecodes)
** See Copyright Notice in lua.h
*/

#define luac_c
#define LUA_CORE

#include "lprefix.h"

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lua.h"
#include "lauxlib.h"

#include "lobject.h"
#include "lstate.h"
#include "lundump.h"

static void PrintFunction( const Proto* f, int full );
#define luaU_print  PrintFunction

#define PROGNAME    "luac"      /* default program name */
#define OUTPUT      PROGNAME ".out" /* default output file */

static int listing = 0;         /* list bytecodes? */
static int dumping = 1;         /* dump bytecodes? */
static int stripping = 0;       /* strip debug information? */
static char Output[] = { OUTPUT };  /* default output file name */
static const char* output = Output; /* actual output file name */
static const char* progname = PROGNAME; /* actual program name */

static void fatal( const char* message ) {
    fprintf( stderr, "%s: %s\n", progname, message );
    exit( EXIT_FAILURE );
}

static void cannot( const char* what ) {
    fprintf( stderr, "%s: cannot %s %s: %s\n", progname, what, output,
             strerror( errno ) );
    exit( EXIT_FAILURE );
}

static void usage( const char* message ) {
    if ( *message == '-' ) {
        fprintf( stderr, "%s: unrecognized option '%s'\n", progname, message );
    }
    else {
        fprintf( stderr, "%s: %s\n", progname, message );
    }

    fprintf( stderr,
             "usage: %s [options] [filenames]\n"
             "Available options are:\n"
             "  -l       list (use -l -l for full listing)\n"
             "  -o name  output to file 'name' (default is \"%s\")\n"
             "  -p       parse only\n"
             "  -s       strip debug information\n"
             "  -v       show version information\n"
             "  --       stop handling options\n"
             "  -        stop handling options and process stdin\n"
             , progname, Output );
    exit( EXIT_FAILURE );
}

#define IS(s)   (strcmp(argv[i],s)==0)

static int doargs( int argc, char* argv[] ) {
    int i;
    int version = 0;

    if ( argv[0] != NULL && *argv[0] != 0 ) {
        progname = argv[0];
    }

    for ( i = 1; i < argc; i++ ) {
        if ( *argv[i] != '-' ) {      /* end of options; keep it */
            break;
        }
        else if ( IS( "--" ) ) {      /* end of options; skip it */
            ++i;

            if ( version ) {
                ++version;
            }

            break;
        }
        else if ( IS( "-" ) ) {   /* end of options; use stdin */
            break;
        }
        else if ( IS( "-l" ) ) {      /* list */
            ++listing;
        }
        else if ( IS( "-o" ) ) {      /* output file */
            output = argv[++i];

            if ( output == NULL || *output == 0 || ( *output == '-' && output[1] != 0 ) ) {
                usage( "'-o' needs argument" );
            }

            if ( IS( "-" ) ) {
                output = NULL;
            }
        }
        else if ( IS( "-p" ) ) {      /* parse only */
            dumping = 0;
        }
        else if ( IS( "-s" ) ) {      /* strip debug information */
            stripping = 1;
        }
        else if ( IS( "-v" ) ) {      /* show version */
            ++version;
        }
        else {                /* unknown option */
            usage( argv[i] );
        }
    }

    if ( i == argc && ( listing || !dumping ) ) {
        dumping = 0;
        argv[--i] = Output;
    }

    if ( version ) {
        printf( "%s\n", LUA_COPYRIGHT );

        if ( version == argc - 1 ) {
            exit( EXIT_SUCCESS );
        }
    }

    return i;
}

#define FUNCTION "(function()end)();"

static const char* reader( lua_State* L, void* ud, size_t* size ) {
    UNUSED( L );

    if ( ( *( int* )ud )-- ) {
        *size = sizeof( FUNCTION ) - 1;
        return FUNCTION;
    }
    else {
        *size = 0;
        return NULL;
    }
}

#define toproto(L,i) getproto(L->top+(i))

static const Proto* combine( lua_State* L, int n ) {
    if ( n == 1 ) {
        return toproto( L, -1 );
    }
    else {
        Proto* f;
        int i = n;

        if ( lua_load( L, reader, &i, "=(" PROGNAME ")", NULL ) != LUA_OK ) {
            fatal( lua_tostring( L, -1 ) );
        }

        f = toproto( L, -1 );

        for ( i = 0; i < n; i++ ) {
            f->p[i] = toproto( L, i - n - 1 );

            if ( f->p[i]->sizeupvalues > 0 ) {
                f->p[i]->upvalues[0].instack = 0;
            }
        }

        f->sizelineinfo = 0;
        return f;
    }
}

static int writer( lua_State* L, const void* p, size_t size, void* u ) {
    UNUSED( L );
    return ( fwrite( p, size, 1, ( FILE* )u ) != 1 ) && ( size != 0 );
}

static int pmain( lua_State* L ) {
    int argc = ( int )lua_tointeger( L, 1 );
    char** argv = ( char** )lua_touserdata( L, 2 );
    const Proto* f;
    int i;

    if ( !lua_checkstack( L, argc ) ) {
        fatal( "too many input files" );
    }

    for ( i = 0; i < argc; i++ ) {
        const char* filename = IS( "-" ) ? NULL : argv[i];

        if ( luaL_loadfile( L, filename ) != LUA_OK ) {
            fatal( lua_tostring( L, -1 ) );
        }
    }

    f = combine( L, argc );

    if ( listing ) {
        luaU_print( f, listing > 1 );
    }

    if ( dumping ) {
        FILE* D = ( output == NULL ) ? stdout : fopen( output, "wb" );

        if ( D == NULL ) {
            cannot( "open" );
        }

        lua_lock( L );
        luaU_dump( L, f, writer, D, stripping );
        lua_unlock( L );

        if ( ferror( D ) ) {
            cannot( "write" );
        }

        if ( fclose( D ) ) {
            cannot( "close" );
        }
    }

    return 0;
}

int main( int argc, char* argv[] ) {
    lua_State* L;
    int i = doargs( argc, argv );
    argc -= i;
    argv += i;

    if ( argc <= 0 ) {
        usage( "no input files given" );
    }

    L = luaL_newstate();

    if ( L == NULL ) {
        fatal( "cannot create state: not enough memory" );
    }

    lua_pushcfunction( L, &pmain );
    lua_pushinteger( L, argc );
    lua_pushlightuserdata( L, argv );

    if ( lua_pcall( L, 2, 0, 0 ) != LUA_OK ) {
        fatal( lua_tostring( L, -1 ) );
    }

    lua_close( L );
    return EXIT_SUCCESS;
}

/*
** $Id: luac.c,v 1.75 2015/03/12 01:58:27 lhf Exp $
** print bytecodes
** See Copyright Notice in lua.h
*/

#include <ctype.h>
#include <stdio.h>

#define luac_c
#define LUA_CORE

#include "ldebug.h"
#include "lobject.h"
#include "lopcodes.h"

#define VOID(p)     ((const void*)(p))

static void PrintString( const TString* ts ) {
    const char* s = getstr( ts );
    size_t i, n = tsslen( ts );
    printf( "%c", '"' );

    for ( i = 0; i < n; i++ ) {
        int c = ( int )( unsigned char )s[i];

        switch ( c ) {
        case '"':
            printf( "\\\"" );
            break;

        case '\\':
            printf( "\\\\" );
            break;

        case '\a':
            printf( "\\a" );
            break;

        case '\b':
            printf( "\\b" );
            break;

        case '\f':
            printf( "\\f" );
            break;

        case '\n':
            printf( "\\n" );
            break;

        case '\r':
            printf( "\\r" );
            break;

        case '\t':
            printf( "\\t" );
            break;

        case '\v':
            printf( "\\v" );
            break;

        default:
            if ( isprint( c ) ) {
                printf( "%c", c );
            }
            else {
                printf( "\\%03d", c );
            }
        }
    }

    printf( "%c", '"' );
}

static void PrintConstant( const Proto* f, int i ) {
    const TValue* o = &f->k[i];

    switch ( ttype( o ) ) {
    case LUA_TNIL:
        printf( "nil" );
        break;

    case LUA_TBOOLEAN:
        printf( bvalue( o ) ? "true" : "false" );
        break;

    case LUA_TNUMFLT: {
        char buff[100];
        sprintf( buff, LUA_NUMBER_FMT, fltvalue( o ) );
        printf( "%s", buff );

        if ( buff[strspn( buff, "-0123456789" )] == '\0' ) {
            printf( ".0" );
        }

        break;
    }

    case LUA_TNUMINT:
        printf( LUA_INTEGER_FMT, ivalue( o ) );
        break;

    case LUA_TSHRSTR:
    case LUA_TLNGSTR:
        PrintString( tsvalue( o ) );
        break;

    default:              /* cannot happen */
        printf( "? type=%d", ttype( o ) );
        break;
    }
}

#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
#define MYK(x)      (-1-(x))

static void PrintCode( const Proto* f ) {
    const Instruction* code = f->code;
    int pc, n = f->sizecode;

    for ( pc = 0; pc < n; pc++ ) {
        Instruction i = code[pc];
        OpCode o = GET_OPCODE( i );
        int a = GETARG_A( i );
        int b = GETARG_B( i );
        int c = GETARG_C( i );
        int ax = GETARG_Ax( i );
        int bx = GETARG_Bx( i );
        int sbx = GETARG_sBx( i );
        int line = getfuncline( f, pc );
        printf( "\t%d\t", pc + 1 );

        if ( line > 0 ) {
            printf( "[%d]\t", line );
        }
        else {
            printf( "[-]\t" );
        }

        printf( "%-9s\t", luaP_opnames[o] );

        switch ( getOpMode( o ) ) {
        case iABC:
            printf( "%d", a );

            if ( getBMode( o ) != OpArgN ) {
                printf( " %d", ISK( b ) ? ( MYK( INDEXK( b ) ) ) : b );
            }

            if ( getCMode( o ) != OpArgN ) {
                printf( " %d", ISK( c ) ? ( MYK( INDEXK( c ) ) ) : c );
            }

            break;

        case iABx:
            printf( "%d", a );

            if ( getBMode( o ) == OpArgK ) {
                printf( " %d", MYK( bx ) );
            }

            if ( getBMode( o ) == OpArgU ) {
                printf( " %d", bx );
            }

            break;

        case iAsBx:
            printf( "%d %d", a, sbx );
            break;

        case iAx:
            printf( "%d", MYK( ax ) );
            break;
        }

        switch ( o ) {
        case OP_LOADK:
            printf( "\t; " );
            PrintConstant( f, bx );
            break;

        case OP_GETUPVAL:
        case OP_SETUPVAL:
            printf( "\t; %s", UPVALNAME( b ) );
            break;

        case OP_GETTABUP:
            printf( "\t; %s", UPVALNAME( b ) );

            if ( ISK( c ) ) {
                printf( " " );
                PrintConstant( f, INDEXK( c ) );
            }

            break;

        case OP_SETTABUP:
            printf( "\t; %s", UPVALNAME( a ) );

            if ( ISK( b ) ) {
                printf( " " );
                PrintConstant( f, INDEXK( b ) );
            }

            if ( ISK( c ) ) {
                printf( " " );
                PrintConstant( f, INDEXK( c ) );
            }

            break;

        case OP_GETTABLE:
        case OP_SELF:
            if ( ISK( c ) ) {
                printf( "\t; " );
                PrintConstant( f, INDEXK( c ) );
            }

            break;

        case OP_SETTABLE:
        case OP_ADD:
        case OP_SUB:
        case OP_MUL:
        case OP_POW:
        case OP_DIV:
        case OP_IDIV:
        case OP_BAND:
        case OP_BOR:
        case OP_BXOR:
        case OP_SHL:
        case OP_SHR:
        case OP_EQ:
        case OP_LT:
        case OP_LE:
            if ( ISK( b ) || ISK( c ) ) {
                printf( "\t; " );

                if ( ISK( b ) ) {
                    PrintConstant( f, INDEXK( b ) );
                }
                else {
                    printf( "-" );
                }

                printf( " " );

                if ( ISK( c ) ) {
                    PrintConstant( f, INDEXK( c ) );
                }
                else {
                    printf( "-" );
                }
            }

            break;

        case OP_JMP:
        case OP_FORLOOP:
        case OP_FORPREP:
        case OP_TFORLOOP:
            printf( "\t; to %d", sbx + pc + 2 );
            break;

        case OP_CLOSURE:
            printf( "\t; %p", VOID( f->p[bx] ) );
            break;

        case OP_SETLIST:
            if ( c == 0 ) {
                printf( "\t; %d", ( int )code[++pc] );
            }
            else {
                printf( "\t; %d", c );
            }

            break;

        case OP_EXTRAARG:
            printf( "\t; " );
            PrintConstant( f, ax );
            break;

        default:
            break;
        }

        printf( "\n" );
    }
}

#define SS(x)   ((x==1)?"":"s")
#define S(x)    (int)(x),SS(x)

static void PrintHeader( const Proto* f ) {
    const char* s = f->source ? getstr( f->source ) : "=?";

    if ( *s == '@' || *s == '=' ) {
        s++;
    }
    else if ( *s == LUA_SIGNATURE[0] ) {
        s = "(bstring)";
    }
    else {
        s = "(string)";
    }

    printf( "\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
            ( f->linedefined == 0 ) ? "main" : "function", s,
            f->linedefined, f->lastlinedefined,
            S( f->sizecode ), VOID( f ) );
    printf( "%d%s param%s, %d slot%s, %d upvalue%s, ",
            ( int )( f->numparams ), f->is_vararg ? "+" : "", SS( f->numparams ),
            S( f->maxstacksize ), S( f->sizeupvalues ) );
    printf( "%d local%s, %d constant%s, %d function%s\n",
            S( f->sizelocvars ), S( f->sizek ), S( f->sizep ) );
}

static void PrintDebug( const Proto* f ) {
    int i, n;
    n = f->sizek;
    printf( "constants (%d) for %p:\n", n, VOID( f ) );

    for ( i = 0; i < n; i++ ) {
        printf( "\t%d\t", i + 1 );
        PrintConstant( f, i );
        printf( "\n" );
    }

    n = f->sizelocvars;
    printf( "locals (%d) for %p:\n", n, VOID( f ) );

    for ( i = 0; i < n; i++ ) {
        printf( "\t%d\t%s\t%d\t%d\n",
                i, getstr( f->locvars[i].varname ), f->locvars[i].startpc + 1,
                f->locvars[i].endpc + 1 );
    }

    n = f->sizeupvalues;
    printf( "upvalues (%d) for %p:\n", n, VOID( f ) );

    for ( i = 0; i < n; i++ ) {
        printf( "\t%d\t%s\t%d\t%d\n",
                i, UPVALNAME( i ), f->upvalues[i].instack, f->upvalues[i].idx );
    }
}

static void PrintFunction( const Proto* f, int full ) {
    int i, n = f->sizep;
    PrintHeader( f );
    PrintCode( f );

    if ( full ) {
        PrintDebug( f );
    }

    for ( i = 0; i < n; i++ ) {
        PrintFunction( f->p[i], full );
    }
}
